/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;

import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Streams;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * HTTP 上传类
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class HttpUploader implements HttpdConstants, AutoCloseable
{
    private static final String CONTENT_DISPOSITION = "Content-Disposition:";
    private static final String CONTENT_TYPE = "Content-Type:";
        
    private HashMap<String, String> parameters;
    
    private String name;
    private String fileContentType;
    private String fileName;
    private ByteArrayOutputStream fileContent;
    
    public HttpUploader()
    {
        parameters = new HashMap<String, String>();
        fileContent = new ByteArrayOutputStream();
    }
    
    public String getParameter(String name)
    {
        return parameters.get(name);
    }
    
    public HashMap<String, String> getParameters()
    {
        return parameters;
    }
    
    public String getName()
    {
        return name;
    }
    
    public String getFileName()
    {
        return fileName;
    }
    
    public String getFileExt()
    {
        return Files.getFileExt(fileName);
    }
    
    public String getFileContentType()
    {
        return fileContentType;
    }
    
    public byte[] getBytes()
    {
        return fileContent.toByteArray();
    }
    
    public int getFileSize()
    {
        return fileContent.size();
    }
    
    public void writeTo(OutputStream out) throws IOException
    {
        fileContent.writeTo(out);
    }
    
    public void writeToFile(String filePath) throws IOException
    {
        Files.write(filePath, fileContent.toByteArray());
    }
    
    public InputStream getInputStream()
    {
        return new ByteArrayInputStream(fileContent.toByteArray());
    }
    
    public void close()
    {
        try{fileContent.close();}catch (IOException e){}
        fileContent = null;
    }
    
    public void parseHttpRequest(HttpRequest request, int maxSize) throws Exception
    {
        int contentLength = request.getContentLength();
        if (contentLength == 0)
            throw new Exception("非法的文件上传信息[contentLength == 0]");
        
        if (contentLength > maxSize)
            throw new Exception("文件超过大小[contentLength == "+ contentLength +"]");
        
        String contentType = request.getContentType();
        if (contentType == null)
            throw new Exception("非法的文件上传信息[contentType == null]");
        
        if (!contentType.startsWith(_MULTIPART_FORM_DATA_))
            throw new Exception("非法的文件上传信息[contentType == "+ contentType +"]");
        
        int ind = contentType.indexOf("boundary=");
        if (ind == -1)
            throw new Exception("非法的文件上传信息[contentType == "+ contentType +"]");
        
        String boundary = contentType.substring(ind+"boundary=".length());
        byte[] buffer = new byte[4096];
        InputStream input = request.getInputStream();
        
        int len = 0; String name = null;String curContentType = null;
        int type = 0; boolean isSkinEmptyLine = false;boolean isLastBR = false;
        while ((len = Streams.readLine(input, buffer, buffer.length)) != -1) 
        {
            String line = new String(buffer, 0, len, request.getCharacterEncoding());
            if (line.startsWith("--"+boundary+"--"))
            {//全部内容结束
                break;
            }
            
            if (line.startsWith("--"+boundary))
            {//新内容开始
                type = 0;               //重置为未知
                isSkinEmptyLine = false;//重置下一行可以跳过空行
                continue;
            }
            
            if (!isSkinEmptyLine && Validates.isEmptyBlank(line))
            {//跳过内容之前的空行
                isSkinEmptyLine = true;
                continue;
            }

            if (Strings.startsWithIgnoreCase(line, CONTENT_DISPOSITION))
            {//内容配置开始，前面空行不处理
                String formData = line.substring(CONTENT_DISPOSITION.length()).trim();
                name = parseFormDataName(formData);
                if (name == null)
                {//无效的配置
                    isSkinEmptyLine = false;//重置下一行可以跳过空行
                    continue;
                }
                
                String filename = parseFormDataFileName(formData);
                if (filename == null)
                {
                    type = 1;//文本类型
                    if (!parameters.containsKey(name))
                        parameters.put(name, null);
                }
                else
                {
                    type = 2;//文件类型
                    this.name = name;
                    this.fileName = filename;
                }
                continue;
            }
            
            if (Strings.startsWithIgnoreCase(line, CONTENT_TYPE))
            {//内容类型开始，文本类型不一般会指定
                curContentType = line.substring(CONTENT_TYPE.length()).trim();
                if (type == 2)
                    this.fileContentType = curContentType;
                
                isSkinEmptyLine = false;//重置下一行可以跳过空行
                continue;
            }
            
            if (type == 1)
            {//文本参数，仅支持一行，多行表示该参数多个值，空行不处理
                parseFormValue(request, name, line);
                isSkinEmptyLine = false;//重置下一行可以跳过空行
            }
            else if (type == 2)
            {//文件内容
                if (isLastBR)
                {//上一个回车换行加上，如果不能到这里，表示上一个回车换行是结尾
                    fileContent.write(_CRLF_);
                    isLastBR = false;
                }
                
                if (!line.endsWith(_BR_))
                    fileContent.write(buffer, 0, len);
                else
                {//文件如果遇到\r\n，要下一个循环判断是否是结尾
                    fileContent.write(buffer, 0, len-2);
                    isLastBR = true;
                }
            }
            else
            {//未定位到文件还是文本时不处理，一般是多余的\r\n
                
            }
        }
    }
    
    /** 解析form-data */
    private String parseFormDataName(String formData)
    {
        String key = "name=\"";
        int ind = formData.indexOf(key);
        if (ind == -1)
            return null;
        int ind2 = formData.indexOf("\"", ind+key.length());
        if (ind2 == -1)
            return null;
        
        return formData.substring(ind+key.length(), ind2);
    }
    
    /** 解析form-data */
    private String parseFormDataFileName(String formData)
    {
        String key = "filename=\"";
        int ind = formData.indexOf(key);
        if (ind == -1)
            return null;
        int ind2 = formData.indexOf("\"", ind+key.length());
        if (ind2 == -1)
            return null;
        
        String name = formData.substring(ind+key.length(), ind2);
        return name;
    }
    
    private void parseFormValue(HttpRequest request, String name, String line)
    {
        line = line.replaceAll("\r", "");
        line = line.replaceAll("\n", "");
        
        String value = parameters.get(name);
        if (value == null)
        {
            parameters.put(name, line);
            request.setParameterOnCNT(name, line);
        }
        else
        {
            parameters.put(name, value + "," + line);//参数名相同时,使用逗号隔开
            request.setParameterOnCNT(name, value + "," + line);
        }
    }
}
